﻿using System;
using System.Collections;
using System.Net;
using System.Linq;

namespace iReaper.IndexService.Common.WebTools
{
    public class WWEIdentity
    {
        #region consts
        const int DARREALM = 0;
        const int DALOGIN = 1;
        const int DAREG = 2;
        const int PROPERTIES = 3;
        const int GENERALDIR = 4;
        const int HELP = 5;
        const int CONFIGVERSION = 6;
        const string MICROSOFTURL = "https://login.live.com/login.srf?wa=wsignin1.0&rpsnv=11&rver=4.5.2130.0&wp=mcmbi&wreply=https:%2f%2fmsevents.microsoft.com%2fcui%2fdefault.aspx%3fculture%3den-us&lc=1033&cb=wizid%3df4502d34-3b8f-4a04-b741-289e08aa1782%26brand%3dmicrosoft%26returnurl%3dhttps%253a%252f%252fmsevents.microsoft.com%252fcui%252fdefault.aspx%253fculture%253den-us%26wp%3dmcmbi%26lcid%3d1033&id=74335";
        #endregion

        #region static
        private static ArrayList PassportUrls;
        private static bool isInited;

        /// <summary>
        /// Get login server address
        /// </summary>
        /// <returns></returns>
        static WWEIdentity()
        {
            isInited = GetLoginServerAddres();
        }
        /// <summary>
        /// This function asks a valid login adres, to connect to
        /// </summary>
        /// <returns>true if succeed</returns>
        private static bool GetLoginServerAddres()
        {
            // Make a request to the server, this adresses are being used in the MSN messenger
            HttpWebRequest ServerRequest = (HttpWebRequest)WebRequest.Create("https://nexus.passport.com/rdr/pprdr.asp");
            // Get the postData
            HttpWebResponse ServerResponse = (HttpWebResponse)ServerRequest.GetResponse();
            if (ServerResponse.StatusCode == HttpStatusCode.OK)
            {
                string retrieveddata = ServerResponse.Headers.ToString();
                PassportUrls = new ArrayList();
                // Pick up the header en split
                string[] result = ServerResponse.Headers.Get("PassportURLs").Split(',');
                foreach (string s in result)
                {
                    // The actual adres is provided behind the '=' sign
                    PassportUrls.Add(s.Substring(s.IndexOf('=') + 1));
                }
                ServerResponse.Close();
                return true;
            }
            else
            {
                ServerResponse.Close();
                return false;
            }
        }


        /// <summary>
        /// This function connects to a login server to request a valid ticket,
        /// that will be used to login on the MSN servers
        /// </summary>
        /// <param name="Password">The password of the user, this is just plain text. The connection is HTTPS
        /// <param name="Username">The complete username</param>
        /// <param name="ChallengeString">A challenge string that you have got, wile connecting to a msn server
        /// <returns>a valid ticket, that you send back to the server to get connected</returns>
        private static string GetClientTicket(string Password, string Username, string ChallengeString, out CookieContainer oCookies)
        {
            oCookies = new CookieContainer();
            // First get a valid login adres for the initial server
            if (isInited)
            {
                // On the position of DALOGIN is a valid URL, for login
                string uri = "https://" + PassportUrls[DALOGIN];
                HttpWebRequest ServerRequest;
                HttpWebResponse ServerResponse;
                try
                {
                    while (true)
                    {

                        //Console.WriteLine("Connecting to:  " + uri);
                        // Make a new request
                        ServerRequest = (HttpWebRequest)HttpWebRequest.Create(uri);
                        ServerRequest.AllowAutoRedirect = false;
                        ServerRequest.Pipelined = false;
                        ServerRequest.KeepAlive = false;
                        ServerRequest.ProtocolVersion = new Version(1, 0);
                        ServerRequest.CookieContainer = oCookies;
                        // Send the authentication header
                        ServerRequest.Headers.Add("Authorization", "Passport1.4 OrgVerb=GET,OrgURL=http%3A%2F%2Fmsevents%2Emicrosoft%2Ecom,sign-in=" + Username.Replace("@", "%40") + ",pwd=" + Password + "," + ChallengeString + "\n");
                        // Pick up the postData
                        ServerResponse = (HttpWebResponse)ServerRequest.GetResponse();
                        oCookies.Add(ServerResponse.Cookies);
                        // If the statuscode is OK, then there is a valid return
                        if (ServerResponse.StatusCode == HttpStatusCode.OK)
                        {
                            string setCookies = ServerResponse.Headers[HttpResponseHeader.SetCookie];
                            System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex("PPAuth=(?<PPAuth>[^;]+);");
                            var match = regex.Match(setCookies);
                            string result = null;
                            if (match.Success)
                            {
                                result = match.Groups["PPAuth"].Value;
                            }
                            // Close connection
                            ServerResponse.Close();

                            // Generate a new substring and return it
                            return result;
                        }
                        // If the statuscode is 302, then there is a redirect, read the new adres en connect again
                        else if (ServerResponse.StatusCode == HttpStatusCode.Found)
                        {
                            uri = ServerResponse.Headers.Get("Location");
                        }
                    }
                }
                catch (WebException e)
                {
                    // If the statuscode is 401, then an exeption occurs
                    // Think that your password + username combination is not correct
                    // return number so that calling functions knows what to do
                    if (e.Status == WebExceptionStatus.ProtocolError)
                    {
                        return "401";
                    }
                    else
                    {
                        return "0";
                    }
                }
            }
            return "0";
        }

        #endregion

        #region field

        private Guid[] _passedGuidList;
        private CookieContainer _cookies;
        private bool _isLogon;
        private string _userName;
        private string _password;

        // Create a WWEIdentity
        // By default not logon
        public WWEIdentity(string username, string password)
        {
            _userName = username;
            _password = password;
        }

        public void Logon()
        {
            // Live service first
            CreatePPCookies();

            // Microsoft second
            CreateMSCookies();
        }

        /// <summary>
        /// Get cookies which has credential data
        /// </summary>
        public CookieContainer Cookie { get { return _cookies; } }

        // Check if response cookie contains valid information
        public bool VerifyCookie()
        {
            // Get cookies of msevents
            var cookies = _cookies.GetCookies(new Uri("https://msevents.microsoft.com"));
            // Check if there is MSPAuth and MSPProf
            bool auth = false, prof = false;
            foreach (Cookie c in cookies)
            {
                if (c.Name.ToLower().Equals("mspauth") && !string.IsNullOrEmpty(c.Value))
                {
                    auth = true;
                }
                else if (c.Name.ToLower().Equals("mspprof") && !string.IsNullOrEmpty(c.Value))
                {
                    prof = true;
                }
            }

            if (!auth || !prof)
            {
                Console.WriteLine("Identity broke, re-logon");
                // Something wrong, logon again
                Logon();
                return false;
            }
            return true;
        }
        /// <summary>
        /// Go to live service to do a authentication, get PPAuth under login.live.com
        /// </summary>
        /// <returns></returns>
        private void CreatePPCookies()
        {
            if (!isInited)
            {
                throw new ApplicationException("Not initialized.");
            }

            var ppauth = GetClientTicket(_password, _userName, "", out _cookies);
            if (ppauth == "401")
            {
                throw new ApplicationException("Invalid username or passowrd");
            }
            else if (ppauth == "0")
            {
                throw new ApplicationException("Falt error");
            }
            string uriString = "https://" + PassportUrls[DALOGIN];
            Uri uri = new Uri(uriString);
            Cookie cookie = new Cookie("PPAuth", ppauth);
            cookie.Domain = uri.Host;
            cookie.Secure = true;
            _cookies.Add(cookie);
        }

        /// <summary>
        /// Go to msevents.microsoft.com to get MSPAuth and other related cookies.
        /// </summary>
        private void CreateMSCookies()
        {
            // access msevents to get mspauth and mspprof
            CookieAwareWebClient client = new CookieAwareWebClient();
            client.Cookies = _cookies;
            string content = client.DownloadString(MICROSOFTURL);
            bool retry = true;
            int count = 0;
            while (retry)
            {
                try
                {
                    // Parse html and do the redirect
                    FormParser parser = new FormParser(content);
                    if (parser.Forms == null || parser.Forms.Length == 0)
                    {
                        throw new ApplicationException("Fault error while try to get MSPAuth");
                    }
                    // This is a jscript drived redirection, which contains tickets for msevents
                    var form = parser.Forms[0];
                    var result = form.Submit(client);
#if DEBUG
                    HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
                    doc.LoadHtml(result);

                    string titile = doc.DocumentNode.SelectSingleNode("//title").InnerText;
                    System.Diagnostics.Debug.Assert(!titile.ToLower().Contains("not authenticated"));
#endif
                    return;
                }
                catch (System.Net.WebException webEx)
                {
                    if (webEx.Response is HttpWebResponse)
                    {
                        var httpResponse = (HttpWebResponse)webEx.Response;
                        int statue = (int)httpResponse.StatusCode / 100;
                        if (statue == 6 || statue == 5)
                        {
                            if (count > 5)
                            {
                                throw;
                            }
                            else
                            {
                                retry = true;
                                count++;
                                Console.WriteLine("Failed {0} in CreateMSCookies, waiting 1 minutes and try again", count);
                                // Sleep for 1 minutes
                                System.Threading.Thread.Sleep(new TimeSpan(0, 1, 0));

                            }
                        }
                    }
                    else
                    {
                        throw;
                    }
                }
            }
           
        }

        #endregion
    }
}
